/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the  "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * $Id$
 */
package wx.xml.xalan.xalan.extensions;

import java.util.Vector;

import wx.xml.xalan.xalan.templates.Constants;

/**
 * Used during assembly of a stylesheet to collect the information for each
 * extension namespace that is required during the transformation process
 * to generate an {@link ExtensionHandler}.
 */
public class ExtensionNamespacesManager {
    /**
     * Vector of ExtensionNamespaceSupport objects to be used to generate ExtensionHandlers.
     */
    private Vector m_extensions             = new Vector();
    /**
     * Vector of ExtensionNamespaceSupport objects for predefined ExtensionNamespaces. Elements
     * from this vector are added to the m_extensions vector when encountered in the stylesheet.
     */
    private Vector m_predefExtensions       = new Vector(7);
    /**
     * Vector of extension namespaces for which sufficient information is not yet available to
     * complete the registration process.
     */
    private Vector m_unregisteredExtensions = new Vector();

    /**
     * An ExtensionNamespacesManager is instantiated the first time an extension function or
     * element is found in the stylesheet. During initialization, a vector of ExtensionNamespaceSupport
     * objects is created, one for each predefined extension namespace.
     */
    public ExtensionNamespacesManager() {
        setPredefinedNamespaces();
    }

    /**
     * If necessary, register the extension namespace found compiling a function or
     * creating an extension element.
     * <p>
     * If it is a predefined namespace, create a
     * support object to simplify the instantiate of an appropriate ExtensionHandler
     * during transformation runtime. Otherwise, add the namespace, if necessary,
     * to a vector of undefined extension namespaces, to be defined later.
     */
    public void registerExtension(String namespace) {
        if (namespaceIndex(namespace, m_extensions) == -1) {
            int predef = namespaceIndex(namespace, m_predefExtensions);
            if (predef != -1)
                m_extensions.add(m_predefExtensions.get(predef));
            else if (!(m_unregisteredExtensions.contains(namespace)))
                m_unregisteredExtensions.add(namespace);
        }
    }

    /**
     * Register the extension namespace for an ElemExtensionDecl or ElemFunction,
     * and prepare a support object to launch the appropriate ExtensionHandler at
     * transformation runtime.
     */
    public void registerExtension(ExtensionNamespaceSupport extNsSpt) {
        String namespace = extNsSpt.getNamespace();
        if (namespaceIndex(namespace, m_extensions) == -1) {
            m_extensions.add(extNsSpt);
            if (m_unregisteredExtensions.contains(namespace))
                m_unregisteredExtensions.remove(namespace);
        }

    }

    /**
     * Get the index for a namespace entry in the extension namespace Vector, -1 if
     * no such entry yet exists.
     */
    public int namespaceIndex(String namespace, Vector extensions) {
        for (int i = 0; i < extensions.size(); i++) {
            if (((ExtensionNamespaceSupport) extensions.get(i)).getNamespace().equals(namespace))
                return i;
        }
        return -1;
    }


    /**
     * Get the vector of extension namespaces. Used to provide
     * the extensions table access to a list of extension
     * namespaces encountered during composition of a stylesheet.
     */
    public Vector getExtensions() {
        return m_extensions;
    }

    /**
     * Attempt to register any unregistered extension namespaces.
     */
    public void registerUnregisteredNamespaces() {
        for (int i = 0; i < m_unregisteredExtensions.size(); i++) {
            String                    ns       = (String) m_unregisteredExtensions.get(i);
            ExtensionNamespaceSupport extNsSpt = defineJavaNamespace(ns);
            if (extNsSpt != null)
                m_extensions.add(extNsSpt);
        }
    }

    /**
     * For any extension namespace that is not either predefined or defined
     * by a "component" declaration or exslt function declaration, attempt
     * to create an ExtensionNamespaceSuport object for the appropriate
     * Java class or Java package Extension Handler.
     * <p>
     * Called by StylesheetRoot.recompose(), after all ElemTemplate compose()
     * operations have taken place, in order to set up handlers for
     * the remaining extension namespaces.
     *
     * @param ns The extension namespace URI.
     * @return An ExtensionNamespaceSupport object for this namespace
     * (which defines the ExtensionHandler to be used), or null if such
     * an object cannot be created.
     * @throws javax.xml.transform.TransformerException
     */
    public ExtensionNamespaceSupport defineJavaNamespace(String ns) {
        return defineJavaNamespace(ns, ns);
    }

    public ExtensionNamespaceSupport defineJavaNamespace(String ns, String classOrPackage) {
        if (null == ns || ns.trim().length() == 0) // defensive. I don't think it's needed.  -sb
            return null;

        // Prepare the name of the actual class or package, stripping
        // out any leading "class:".  Next, see if there is a /.  If so,
        // only look at the text to the right of the rightmost /.
        String className = classOrPackage;
        if (className.startsWith("class:"))
            className = className.substring(6);

        int lastSlash = className.lastIndexOf('/');
        if (-1 != lastSlash)
            className = className.substring(lastSlash + 1);

        // The className can be null here, and can cause an error in getClassForName
        // in JDK 1.8.
        if (null == className || className.trim().length() == 0)
            return null;

        try {
            ExtensionHandler.getClassForName(className);
            return new ExtensionNamespaceSupport(
                ns,
                "wx.xml.xalan.xalan.extensions.ExtensionHandlerJavaClass",
                new Object[]{ns, "javaclass", className});
        } catch (ClassNotFoundException e) {
            return new ExtensionNamespaceSupport(
                ns,
                "wx.xml.xalan.xalan.extensions.ExtensionHandlerJavaPackage",
                new Object[]{ns, "javapackage", className + "."});
        }
    }
  
/*
  public ExtensionNamespaceSupport getSupport(int index, Vector extensions)
  {
    return (ExtensionNamespaceSupport)extensions.elementAt(index);
  }
*/


    /**
     * Set up a Vector for predefined extension namespaces.
     */
    private void setPredefinedNamespaces() {
        String uri              = Constants.S_EXTENSIONS_JAVA_URL;
        String handlerClassName = "wx.xml.xalan.xalan.extensions.ExtensionHandlerJavaPackage";
        String lang             = "javapackage";
        String lib              = "";
        m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
            new Object[]{uri, lang, lib}));

        uri = Constants.S_EXTENSIONS_OLD_JAVA_URL;
        m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
            new Object[]{uri, lang, lib}));

        uri = Constants.S_EXTENSIONS_LOTUSXSL_JAVA_URL;
        m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
            new Object[]{uri, lang, lib}));

        uri = Constants.S_BUILTIN_EXTENSIONS_URL;
        handlerClassName = "wx.xml.xalan.xalan.extensions.ExtensionHandlerJavaClass";
        lang = "javaclass"; // for remaining predefined extension namespaces.
        lib = "wx.xml.xalan.xalan.lib.Extensions";
        m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
            new Object[]{uri, lang, lib}));

        uri = Constants.S_BUILTIN_OLD_EXTENSIONS_URL;
        m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
            new Object[]{uri, lang, lib}));

        // Xalan extension namespaces (redirect, pipe and SQL).
        uri = Constants.S_EXTENSIONS_REDIRECT_URL;
        lib = "wx.xml.xalan.xalan.lib.Redirect";
        m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
            new Object[]{uri, lang, lib}));

        uri = Constants.S_EXTENSIONS_PIPE_URL;
        lib = "wx.xml.xalan.xalan.lib.PipeDocument";
        m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
            new Object[]{uri, lang, lib}));

        uri = Constants.S_EXTENSIONS_SQL_URL;
        lib = "wx.xml.xalan.xalan.lib.sql.XConnection";
        m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
            new Object[]{uri, lang, lib}));


        //EXSLT namespaces (not including EXSLT function namespaces which are
        // registered by the associated ElemFunction.
        uri = Constants.S_EXSLT_COMMON_URL;
        lib = "wx.xml.xalan.xalan.lib.ExsltCommon";
        m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
            new Object[]{uri, lang, lib}));

        uri = Constants.S_EXSLT_MATH_URL;
        lib = "wx.xml.xalan.xalan.lib.ExsltMath";
        m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
            new Object[]{uri, lang, lib}));

        uri = Constants.S_EXSLT_SETS_URL;
        lib = "wx.xml.xalan.xalan.lib.ExsltSets";
        m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
            new Object[]{uri, lang, lib}));

        uri = Constants.S_EXSLT_DATETIME_URL;
        lib = "wx.xml.xalan.xalan.lib.ExsltDatetime";
        m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
            new Object[]{uri, lang, lib}));

        uri = Constants.S_EXSLT_DYNAMIC_URL;
        lib = "wx.xml.xalan.xalan.lib.ExsltDynamic";
        m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
            new Object[]{uri, lang, lib}));

        uri = Constants.S_EXSLT_STRINGS_URL;
        lib = "wx.xml.xalan.xalan.lib.ExsltStrings";
        m_predefExtensions.add(new ExtensionNamespaceSupport(uri, handlerClassName,
            new Object[]{uri, lang, lib}));
    }

}
